package com.tsx.template.common.exceptionHandler;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import com.tsx.template.common.vo.Result;
import com.tsx.template.common.constants.StatusCode;
import com.tsx.template.common.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(value = BusinessException.class)
    public Result<String> businessEx(BusinessException ex) {
        log.error("自定义异常", ex);
        return Result.error(ex.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public Result<String> ex(Exception ex) {
        log.error("大异常", ex);
        return Result.error(ex.getMessage());
    }

    // 全局异常拦截（拦截项目中的NotLoginException异常）
    @ExceptionHandler(NotLoginException.class)
    public Result<String> handlerNotLoginException(NotLoginException nle) {
        // 打印堆栈，以供调试
        log.error("登录校验异常", nle);
        // 判断场景值，定制化异常信息
        String message;
        if (nle.getType().equals(NotLoginException.NOT_TOKEN)) {
            message = "未能读取到有效 token";
        } else if (nle.getType().equals(NotLoginException.INVALID_TOKEN)) {
            message = "token 无效";
        } else if (nle.getType().equals(NotLoginException.TOKEN_TIMEOUT)) {
            message = "token 已过期";
        } else if (nle.getType().equals(NotLoginException.BE_REPLACED)) {
            message = "token 已被顶下线";
        } else if (nle.getType().equals(NotLoginException.KICK_OUT)) {
            message = "token 已被踢下线";
        } else if (nle.getType().equals(NotLoginException.TOKEN_FREEZE)) {
            message = "token 已被冻结";
        } else if (nle.getType().equals(NotLoginException.NO_PREFIX)) {
            message = "未按照指定前缀提交 token";
        } else {
            message = "当前会话未登录";
        }
        // 返回给前端
        return Result.error(StatusCode.LOGIN_ERROR, message);
    }

    @ExceptionHandler(NotPermissionException.class)
    public Result<String> notPermissionException(NotPermissionException npe) {
        log.error("权限异常", npe);
        return Result.error(StatusCode.PERMISSION_ERROR, "无权限");
    }

    @ExceptionHandler(DataIntegrityViolationException.class)
    public Result<String> handleDataIntegrityViolationException(DataIntegrityViolationException ex) {
        Throwable cause = ex.getRootCause();
        if (cause instanceof SQLException sqlException) {
            String errorMessage = sqlException.getMessage();
            if (errorMessage.contains("Duplicate entry")) {
                // 解析错误信息，找到重复的字段信息
                int startIndex = errorMessage.indexOf("Duplicate entry '") + "Duplicate entry '".length();
                int endIndex = errorMessage.indexOf("' for key '", startIndex);
                String duplicateValue = errorMessage.substring(startIndex, endIndex);
                return Result.error("已存在" + duplicateValue);
            }
        }
        return Result.error("数据库异常" + ex.getMessage());
    }

    @ExceptionHandler(BindException.class)
    public Result<String> bindExceptionHandler(BindException ex) {
        log.error("异常", ex);
        String error = ex.getAllErrors().stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.joining(";"));
        return Result.error(StatusCode.PARAM_ERROR, error);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<String> methodArgumentNotValidException(MethodArgumentNotValidException ex) {
        log.error("异常", ex);
        String error = ex.getAllErrors().stream()
                .map(ObjectError::getDefaultMessage)
                .collect(Collectors.joining(";"));
        return Result.error(StatusCode.PARAM_ERROR, error);
    }
}
